
#include "ticlog.h"

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 递归删除目录(删除该目录以及该目录包含的文件和目录)
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
int remove_dir(const char *dir)
{
	char cur_dir[] = ".";
	char up_dir[] = "..";
	char dir_name[128];
	DIR *dirp;
	struct dirent *dp;
	struct stat dir_stat;

	// 参数传递进来的目录不存在，直接返回
	if ( 0 != access(dir, F_OK) ) {
		return 0;
	}

	// 获取目录属性失败，返回错误
	if ( 0 > stat(dir, &dir_stat) ) {
		perror("get directory stat error");
		return -1;
	}

	if ( S_ISREG(dir_stat.st_mode) ) {	// 普通文件直接删除
		remove(dir);
	} else if ( S_ISDIR(dir_stat.st_mode) ) {	// 目录文件，递归删除目录中内容
		dirp = opendir(dir);
		while ( (dp=readdir(dirp)) != NULL ) {
			// 忽略 . 和 ..
			if ( (0 == strcmp(cur_dir, dp->d_name)) || (0 == strcmp(up_dir, dp->d_name)) ) {
				continue;
			}

			sprintf(dir_name, "%s/%s", dir, dp->d_name);
			remove_dir(dir_name);   // 递归调用
		}
		closedir(dirp);

		rmdir(dir);		// 删除空目录
	} else {
		perror("unknow file type!");
	}

	return 0;
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 判断是否为标志的日志文件夹
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
bool identifyFileName(const char *name)
{
    int sw_ca = 0;
    for(int i=0; i<256; i++)
    {
        if(sw_ca == 5)
            if(name[i] == 0)
                return true;

        if(!((name[i]=='_') || (name[i]>='0' && name[i]<='9')))
        {
            return false;
        }

        switch(sw_ca)
        {

            case 0: //年
                if(name[i]>='0' && name[i]<='9') sw_ca = 1;
                break;
            case 1:
                if(name[i] == '_') sw_ca = 2;
                break;
            case 2://月
                if(name[i]>='0' && name[i]<='9') sw_ca = 3;
                break;
            case 3:
                if(name[i] == '_') sw_ca = 4;
                break;
            case 4://日
                if(name[i]>='0' && name[i]<='9') sw_ca = 5;
                break;
            case 5:
                if(name[i] == 0) return true;
                break;
            default:
                break;
        }
    }

    return false;
}

struct tmDate_Type
{
    int day;
    int mon;
    int year;
};

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 将日志文件名转换为时间
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
struct tmDate_Type getLogFileNameDate(const char *name)
{
    struct tmDate_Type ret;
    int sw_ca = 0;
    char temp[3][10] = {0};
    int j = 0;
    for(int i=0; i<256; i++)
    {
        switch(sw_ca)
        {
            case 0: //年
                if(name[i]>='0' && name[i]<='9')
                {
                    temp[0][j] = name[i];
                    j++;
                }
                else if(name[i] == '_')
                {
                    sw_ca = 1;
                    j = 0;
                }
                else
                {
                    //error
                    ret.day = 0;
                    ret.mon = 0;
                    ret.year = 0;
                    return ret;
                }
                break;
            case 1:
                if(name[i]>='0' && name[i]<='9')
                {
                    temp[1][j] = name[i];
                    j++;
                }
                else if(name[i] == '_')
                {
                    sw_ca = 2;
                    j = 0;
                }
                else
                {
                    //error
                    ret.day = 0;
                    ret.mon = 0;
                    ret.year = 0;
                    return ret;
                }
                break;
            case 2:
                if(name[i]>='0' && name[i]<='9')
                {
                    temp[2][j] = name[i];
                    j++;
                }
                else if(name[i] == 0)
                {
                    sw_ca = 3;
                    j = 0;
                }
                else
                {
                    //error
                    ret.day = 0;
                    ret.mon = 0;
                    ret.year = 0;
                    return ret;
                }
                break;
            case 3:
                ret.day = atoi(temp[2]);
                ret.mon = atoi(temp[1]);
                ret.year = atoi(temp[0]);
                return ret;
                break;
            default:
                break;
        }
    }

    return ret;
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : data1 > data2 返回 true 否则返回false
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
bool timeCompare(struct tmDate_Type data1, struct tmDate_Type data2)
{
    if(data1.year > data2.year) return true;
    else if(data1.year < data2.year) return false;
    else if(data1.mon > data2.mon) return true;
    else if(data1.mon < data2.mon) return false;
    else if(data1.day > data2.day) return true;
    else if(data1.day < data2.day) return false;
    return false;
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 两个结构体进行数据交换
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
void timeSwab(struct tmDate_Type &data1, struct tmDate_Type &data2)
{
    struct tmDate_Type temp;

    temp.day = data1.day;
    temp.mon = data1.mon;
    temp.year = data1.year;

    data1.day = data2.day;
    data1.mon = data2.mon;
    data1.year = data2.year;

    data2.day = temp.day;
    data2.mon = temp.mon;
    data2.year = temp.year;
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 根据文件名自动删除旧的文件名
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
std::string autoRemoveLogName(const char *dir)
{
	char cur_dir[] = ".";
	char up_dir[] = "..";
	char dir_name[128];
	DIR *dirp;
	struct dirent *dp;
	struct stat dir_stat;
    struct tmDate_Type log_file_name_arry[LOG_SAVE_NUMBER] = {0};

	// 参数传递进来的目录不存在，直接返回
	if ( 0 != access(dir, F_OK) ) {
		return "NULL";
	}

	// 获取目录属性失败，返回错误
	if ( 0 > stat(dir, &dir_stat) ) {
		perror("get directory stat error");
		return "NULL";
	}

	if ( S_ISREG(dir_stat.st_mode) ) // 普通文件
    {

	}
    else if ( S_ISDIR(dir_stat.st_mode) ) // 目录文件
    {
		dirp = opendir(dir);
        int file_number = 0; //符合调节的文件数目
		while ( (dp=readdir(dirp)) != NULL )
        {
			// 忽略 . 和 ..
			if ( (0 == strcmp(cur_dir, dp->d_name)) || (0 == strcmp(up_dir, dp->d_name)) ) {
				continue;
			}

            if(identifyFileName(dp->d_name))
            {
                struct tmDate_Type date = getLogFileNameDate(dp->d_name);
                for(int i=0; i<LOG_SAVE_NUMBER; i++)
                {
                    if(timeCompare(date,log_file_name_arry[i]))
                    {
                        for(int j=i; j<LOG_SAVE_NUMBER; j++)
                        {
                            timeSwab(date,log_file_name_arry[j]);
                        }

                        sprintf(dir_name, "%s/%d_%02d_%02d", dir, date.year, date.mon, date.day);
                        break;

                    }
                    else if(i == (LOG_SAVE_NUMBER-1))
                    {
                        sprintf(dir_name, "%s/%d_%02d_%02d", dir, date.year, date.mon, date.day);
                    }
                }

                //删除文件
                //std::cout << " >>>remove" << dir << " >>>" << dp->d_name << " >>>" << dir_name << std::endl;
                remove_dir(dir_name);
            }
            else
            {
                //不符合调节的文件夹
            }

		}
		closedir(dirp);
	} else
    {
		perror("unknow file type!");
	}

    return "NULL";
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 获取可执行文件名
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
int get_exec_name(char *name,int name_size)
{
	char path[1024]={0};
	int ret = readlink("/proc/self/exe",path,sizeof(path)-1);
	if(ret == -1)
	{
		printf("---- get exec name fail!!\n");
		return -1;
	}
	path[ret]= '\0';

	char *ptr = strrchr(path,'/');
	bzero(name,name_size); //清空缓存区
	strncpy(name,ptr+1,name_size-1);
	return 0;
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 获取日志文件名
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
std::string getLogFileName()
{
    std::string file_name;
    file_name = ros::this_node::getName();
    if(file_name == "empty")
    {
        char path[256]={0};
	    get_exec_name(path,sizeof(path)); //获取可执行文件名
        file_name = path;
    }
    file_name += ".log";
    return file_name;
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 获取日志存储路径
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
std::string getLogFilePath()
{
    //日志路径
    std::string file_path;
    struct passwd *pwd = nullptr;
    pwd = getpwuid(getuid());
    file_path = pwd->pw_name;
    if(file_path != "root")
    {
        file_path = "/home/"+file_path+"/log/";
    }
    else
    {
        file_path = "/"+file_path+"/log/";
    }

    //删除旧的日志文件
    autoRemoveLogName(file_path.c_str());

    //按照日期存储日志
    time_t nSeconds;
    struct tm *time_data;
    time(&nSeconds); //获取时间
    time_data = localtime(&nSeconds);
    char temp[40];
    sprintf(temp, "%04d_%02d_%02d/",time_data->tm_year + 1900, time_data->tm_mon + 1, time_data->tm_mday);
    file_path += temp;

    if(access(file_path.c_str(),0)==-1)  //access函数是查看文件是不是存在
    {
        if (mkdir(file_path.c_str(),0777)) //如果不存在就用mkdir函数来创建
        {
            printf("creat file bag failed!!!\n");
        }
    }

    return file_path;
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 日志系统初始化
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
char *initLogSyatem(void)
{
    static char log_file[200];
    static bool init_flag = false;
    if(init_flag) return log_file;

    //日志路径
    std::string file_path = getLogFilePath();
    //日志文件名
    std::string file_name = getLogFileName();

    sprintf(log_file,"%s%s",file_path.c_str(),file_name.c_str());
    init_flag = true;
    return log_file;
}

/****************************************
* 作者/公司  : 有志小青年
* 函数介绍   : 保存日志
* 输入参数   :
* 输出参数   :
* 返回值     :
*****************************************/
void ticSaveLogToFile(int8_t leve, const char *file, int line, const char *format, ...)
{
    if(file == nullptr) return;
    if(format == nullptr) return;

    va_list args;
    va_start(args,format);

    char *logfile = initLogSyatem();
    FILE* fp = NULL;
    if( (fp=fopen(logfile,"ab+")) == NULL ){
        printf("open file error\n");
        exit(0);
    }

    switch(leve)
    {
        case 0: fprintf(fp,"\nflie:\"%s\" :%d debug : ", file, line); break;
        case 1: fprintf(fp,"\nflie:\"%s\" :%d info : ", file, line); break;
        case 2: fprintf(fp,"\nflie:\"%s\" :%d warn : ", file, line); break;
        case 3: fprintf(fp,"\nflie:\"%s\" :%d error : ", file, line); break;
        case 4: fprintf(fp,"\nflie:\"%s\" :%d fatal : ", file, line); break;
        case 5: fprintf(fp,"\nflie:\"%s\" :%d error : ", file, line); break;
        default: break;
    }

    vfprintf(fp,format,args);
    va_end(args);
    //fflush(fp); //将数据立即写入硬盘
    fclose(fp);

}
